package de.koller.oceanFramework.math;

public class RVec3 {
	public float x, y, z;
	
	public RVec3() {
		this.clear();
	}
	
	public RVec3( float x, float y, float z ) {
		this.set( x, y, z );
	}
	
	public RVec3( RVec3 o ) {
		this.set( o );
	}
	
	public final RVec3 set( RVec3 o ) {
		this.x = o.x;
		this.y = o.y;
		this.z = o.z;
		return this;
	}
	
	public final RVec3 set( float x, float y, float z ) {
		this.x = x;
		this.y = y;
		this.z = z;
		return this;
	}
	
	public final RVec3 clear() {
		this.x = 0;
		this.y = 0;
		this.z = 0;
		return this;
	}
	
	public final RVec3 add( RVec3 o ) {
		this.x += o.x;
		this.y += o.y;
		this.z += o.z;
		return this;
	}
	
	public final RVec3 sub( RVec3 o ) {
		this.x -= o.x;
		this.y -= o.y;
		this.z -= o.z;
		return this;
	}
	
	public final RVec3 mult( float s ) {
		this.x *= s;
		this.y *= s;
		this.z *= s;
		return this;
	}
	
	public static RVec3 difference( RVec3 a, RVec3 b ) {
		return a.newInstance().sub( b );
	}
	
	public final RVec3 negate() {
		this.x = -this.x;
		this.y = -this.y;
		this.z = -this.z;
		return this;
	}
	
	public final RVec3 newInstance() {
		return new RVec3( this );
	}
	
	@Override
	public String toString() {
		return "( " + x + ", " + y + ", " + z + " )";
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Float.floatToIntBits(x);
		result = prime * result + Float.floatToIntBits(y);
		result = prime * result + Float.floatToIntBits(z);
		return result;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		RVec3 other = (RVec3)obj;
		if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x))
			return false;
		if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y))
			return false;
		if (Float.floatToIntBits(z) != Float.floatToIntBits(other.z))
			return false;
		return true;
	}
	
	public static float dot( RVec3 a, RVec3 b ) {
		return a.x * b.x + a.y * b.y + a.z * b.z;
	}
	
	public static RVec3 cross( RVec3 a, RVec3 b ) {
		return new RVec3( a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x );
	}
	
	public final float getLength() {
        return (float)Math.sqrt( this.getLengthSquared() );
    }

    public final float getLengthSquared() {
        return x * x + y * y + z * z;
    }
	
	public final RVec3 normalize() {
		float length = getLengthSquared();
        if( length != 1f && length != 0f ) {
            length = 1f / (float)Math.sqrt( length );
            return this.mult( length );
        } 
		return this;
	}
	
	public static float getAngle( RVec3 a, RVec3 b ) {
        float dotProduct = dot( a, b );
        float angle = (float)Math.acos( dotProduct );
        return angle;
    }
	
	public RVec3 reflectExtern( RVec3 normal ) {
		RVec3 n = normal.newInstance().normalize();
		float k = -2f * RVec3.dot( this, n );
		return n.mult( k ).add( this );
	}
	
}
